package com.mes.cloud.util;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * TreeUtils
 *
 * @Author:WenJun Fan
 * @Date: 2018/4/18
 */
public class TreeUtils {

    /**
     * @methodName getTree
     * @date 2018/4/20 10:41
     * @param list, idName, parentIdName, listChildrenName
     * @return java.util.List<T>
     * 转换成对象树
     */
    public static <T> List<T> getTree(List<T> list,String idName,String parentIdName,String listChildrenName) {
        Map<String,Object> ids = Maps.newHashMap();
        List<T> data = new ArrayList<>();
        for (T t : list) {
            Class<?> clazz = t.getClass();
            try {
                Field id = clazz.getDeclaredField(idName);
                id.setAccessible(true);
                Field children = clazz.getDeclaredField(listChildrenName);
                children.setAccessible(true);
                children.set(t,new ArrayList<T>());
                ids.put(id.get(t).toString(),t);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        for (T t : list) {
            Class<?> clazz = t.getClass();
            try {
                Field parentId = clazz.getDeclaredField(parentIdName);
                parentId.setAccessible(true);
                Object o = parentId.get(t);
                Object t1 = ids.get(o.toString());
                if(t1 == null){
                    data.add(t);
                }else{
                    Field children = t1.getClass().getDeclaredField(listChildrenName);
                    children.setAccessible(true);
                    List<Object> child = (List<Object>) children.get(t1);
                    child.add(t);
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return data;
    }

    /**
     * @methodName getTreeMap
     * @date 2018/4/20 10:41
     * @param list, idName, parentIdName, listChildrenName
     * @return java.util.List<java.util.Map<java.lang.Object,java.lang.Object>>
     * 转换成集合树
     */
    public static <T> List<Map<Object,Object>> getTreeMap(List<T> list,String idName,String parentIdName,String listChildrenName) {
        Map<Object,Object> ids = Maps.newHashMap();
        List<Map<Object,Object>> all = new ArrayList<>();
        List<Map<Object,Object>> data = new ArrayList<>();
        for (T t : list) {
            Map<Object, Object> mapt = getBeanToMap(t, listChildrenName);
            all.add(mapt);
            Class<?> clazz = t.getClass();
            try {
                Field id = clazz.getDeclaredField(idName);
                id.setAccessible(true);
                ids.put(id.get(t),mapt);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        for (Map<Object, Object> map : all) {
            Map<Object, Object> o = (Map<Object, Object>) ids.get(map.get(parentIdName));
            if(o != null){
                List<Map<Object,Object>> children = (List<Map<Object, Object>>) o.get(listChildrenName);
                children.add(map);
            }else{
                data.add(map);
            }
        }


        return data;
    }

    /**
     * @methodName getBeanToMap
     * @date 2018/4/20 10:42
     * @param t, listChildrenName
     * @return java.util.Map<java.lang.Object,java.lang.Object>
     * 对象转换成map，加一个children字段
     */
    private static <T> Map<Object,Object> getBeanToMap(T t,String listChildrenName) {
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Map<Object,Object> map = Maps.newHashMap();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                map.put(field.getName(),field.get(t));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        map.put(listChildrenName,new ArrayList<Map<Object,Object>>());
        return map;
    }

    /**
     * @methodName getTreeToList
     * @date 2018/4/20 11:12
     * @param tree, listChildrenName
     * @return java.util.List<T>
     * 把树转换成list
     */
    public static <T> List<T> getTreeToList(List<T> tree, String listChildrenName){
        List<T> list = new ArrayList<>();
        if(tree != null){
            for (T t : tree) {
                getTreeToListBean(t,listChildrenName,list);
            }
        }
        return list;
    }

    /**
     * @methodName getTreeToListBean
     * @date 2018/4/20 11:13
     * @param t, listChildrenName, list
     * @return void
     * 递归循环把树转换成封装成list的工具方法
     */
    private static <T> void getTreeToListBean(T t, String listChildrenName, List<T> list){
        Class<?> clazz = t.getClass();
        if(!StringUtils.isEmpty(listChildrenName)){
            try {
                Field children = clazz.getDeclaredField(listChildrenName);
                children.setAccessible(true);
                List<T> os = (List<T>) children.get(t);
                if(os != null){
                    for (T o : os) {
                        getTreeToListBean(o,listChildrenName,list);
                    }
                }
                children.set(t,null);
                list.add(t);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * @methodName getTreeToList
     * @date 2018/4/20 11:12
     * @param tree, listChildrenName
     * @return java.util.List<T>
     * 把Map树转换成list
     */
    public static List<Map<Object,Object>> getTreeMapToListMap(List<Map<Object,Object>> tree, String listChildrenName){
        List<Map<Object,Object>> list = new ArrayList<>();
        if(tree != null){
            for (Map<Object,Object> t : tree) {
                getTreeMapToListBeanMap(t,listChildrenName,list);
            }
        }
        return list;
    }

    /**
     * @methodName getTreeToListBean
     * @date 2018/4/20 11:13
     * @param t, listChildrenName, list
     * @return void
     * 递归循环把Map树转换成封装成list的工具方法
     */
    private static void getTreeMapToListBeanMap(Map<Object,Object> t, String listChildrenName, List<Map<Object,Object>> list){
        if(!StringUtils.isEmpty(listChildrenName)){
            List<Map<Object,Object>> os = (List<Map<Object, Object>>) t.get(listChildrenName);
            if(os != null){
                for (Map<Object,Object> o : os) {
                    o.put("parentId",t.get("id"));
                    getTreeMapToListBeanMap(o,listChildrenName,list);
                }
            }
            t.put(listChildrenName,null);
            list.add(t);
        }
    }

    /**
     * @methodName getTreeMapToListMap
     * @date 2018/4/20 14:52
     * @param tree, idName, parentId, listChildrenName
     * @return java.util.List<java.util.Map<java.lang.Object,java.lang.Object>>
     * 常用树转换list
     */
    public static List<Map<Object,Object>> getTreeMapToListMap(List<Map<Object,Object>> tree, String idName, String parentIdName, String listChildrenName){
        List<Map<Object,Object>> list = new ArrayList<>();
        if(tree != null){
            for (Map<Object,Object> t : tree) {
                getTreeMapToListBeanMap(t,idName,parentIdName,listChildrenName,list);
            }
        }
        return list;
    }
    /**
     * @methodName getTreeToListBean
     * @date 2018/4/20 11:13
     * @param t, listChildrenName, list
     * @return void
     * 递归循环把Map树转换成封装成list的工具方法
     */
    private static void getTreeMapToListBeanMap(Map<Object,Object> t,String idName, String parentIdName, String listChildrenName, List<Map<Object,Object>> list){
        if(!StringUtils.isEmpty(listChildrenName)){
            List<Map<Object,Object>> os = (List<Map<Object, Object>>) t.get(listChildrenName);
            if(os != null){
                for (Map<Object,Object> o : os) {
                    o.put(parentIdName,t.get(idName));
                    getTreeMapToListBeanMap(o,idName,parentIdName,listChildrenName,list);
                }
            }
            t.put(listChildrenName,null);
            list.add(t);
        }
    }

    /***
     * @methodName getListByLower
     * @date 2018/5/23 16:55
     * @param list, parentIdname, objectName, methodName
     * @return java.util.List<T>
     * 通过最下级寻找所有
     */
    public static <T> List<T> getListByLower(List<T> list, String parentIdname,Object objectName,String methodName){
        List<T> data = new ArrayList<>();
        if(list != null){
            for (T t : list) {
                data.add(t);
                beanToList(data,t,parentIdname,objectName,methodName);
            }
        }
        HashSet<T> set = new HashSet<>(data);
        data.clear();
        data.addAll(set);
        return data;
    }

    private static <T> void beanToList(List<T> data,T t,String parentIdName ,Object o,String methodName){
        try {
            Field declaredField = t.getClass().getDeclaredField(parentIdName);
            declaredField.setAccessible(true);
            Long parentId = Long.parseLong(declaredField.get(t).toString());
            if(parentId != null && parentId != 0){
                Class<?> clazz = o.getClass();
                Method method = clazz.getDeclaredMethod(methodName, Long.class);
                method.setAccessible(true);
                T dto = (T) method.invoke(o, parentId);
                data.add(dto);
                beanToList(data,dto,parentIdName,o,methodName);
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * @methodName getTreeMapByListMap
     * @date 2018/4/26 17:11
     * @param list, idName, parentIdName, listChildrenName
     * @return java.util.List<java.util.Map<java.lang.Object,java.lang.Object>>
     * 把listmap集合转换成树
     */
    public static List<Map<Object,Object>> getTreeMapByListMap(List<Map<Object,Object>> list,String idName,String parentIdName,String listChildrenName){
        if(list != null && list.size() > 0){
            Map<Object, Map<Object, Object>> ids = Maps.newHashMap();
            List<Map<Object, Object>> tree = new ArrayList<>();
            for (Map<Object, Object> map : list) {
                ids.put(map.get(idName),map);
                map.put(listChildrenName,new ArrayList<Map<Object, Object>>());
            }
            for (Map<Object, Object> map : list) {
                Object parentId = map.get(parentIdName);
                if(!StringUtils.isEmpty(parentId)){
                    Map<Object, Object> line = ids.get(parentId);
                    if(line == null){
                        tree.add(map);
                    }else{
                        List<Map<Object, Object>> data = (List<Map<Object, Object>>) line.get(listChildrenName);
                        data.add(map);
                    }
                }
            }
            return tree;
        }
        return null;
    }


    /***
     * @methodName getListByUpper
     * @date 2018/5/10 11:04
     * @param list
     * @param idName
     * @param parentIdname
     * @param objectName
     * @param methodName
     * @param condition
     * @return java.util.List<T>
     * 通过上级找下级
     */
    public static <T> List<T> getListByUpper(List<T> list, String idName, String parentIdname,Object objectName,String methodName, Map<Object,Object> condition){
        List<T> data = new ArrayList<>();
        if(list != null){
            for (T t : list) {
                data.add(t);
                beanToList(data,t,idName,parentIdname,objectName,methodName,condition);
            }
        }
        HashSet<T> set = new HashSet<>(data);
        data.clear();
        data.addAll(set);
        return data;
    }

    private static <T> void beanToList(List<T> data, T t, String idName, String parentIdname, Object objectName, String methodName, Map<Object, Object> condition) {
        try {
            Class<?> clazz = t.getClass();
            Field fieldId = clazz.getDeclaredField(idName);
            fieldId.setAccessible(true);
            Object id = fieldId.get(t);
            condition.put(parentIdname,id.toString());
            Class<?> methodClazz = objectName.getClass();
            Method method = methodClazz.getDeclaredMethod(methodName, Map.class);
            method.setAccessible(true);
            List<T> invoke = (List<T>) method.invoke(objectName, condition);
            if(invoke != null && invoke.size() > 0){
                for (T t1 : invoke) {
                    data.add(t1);
                    beanToList(data,t1,idName,parentIdname,objectName,methodName,condition);
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    // 汇总数据入口
    public static <T> List<T> sumData(List<T> list,String idName, String parentIdName, List<String> targetSumDataNameList){
        List<T> treeList = handleData(list,idName,parentIdName,targetSumDataNameList);
        countData(treeList,idName,parentIdName,targetSumDataNameList);
        return treeList;
    }

    // 对将要汇总的数据进行处理
    private static <T> List<T> handleData(List<T> list,String idName, String parentIdName, List<String> targetSumDataNameList){
        if(list != null && list.size() > 0) {
            // 取出将最下级和其他数据分开
            Map<String, T> id_object = Maps.newHashMap();
            List<String> parent_id_list = Lists.newArrayList();
            for (T t : list) {
                Map<String, Object> map = gainData(t);
                id_object.put(map.get(idName).toString(), t);
                if (!StringUtils.isEmpty(map.get(parentIdName)) && !"0".equalsIgnoreCase(map.get(parentIdName).toString())) {
                    parent_id_list.add(map.get(parentIdName).toString());
                }
            }
            if (parent_id_list != null && parent_id_list.size() > 0) {
                for (String id : id_object.keySet()) {
                    if (parent_id_list.contains(id)) {
                        try {
                            T t = id_object.get(id);
                            Class<?> clazz = t.getClass();
                            for (String targetSumDataName : targetSumDataNameList) {
                                Field field = clazz.getDeclaredField(targetSumDataName);
                                field.setAccessible(true);
                                field.set(t,0.0);
                            }
                        } catch (NoSuchFieldException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }else {
                        try {
                            T t = id_object.get(id);
                            Class<?> clazz = t.getClass();
                            for (String targetSumDataName : targetSumDataNameList) {
                                Field field = clazz.getDeclaredField(targetSumDataName);
                                field.setAccessible(true);
                                field.set(t,(new BigDecimal(field.get(t).toString())).setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue());
                            }
                        } catch (NoSuchFieldException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return list;
        }
        return null;
    }

    // 对数据进行向上汇总
    private static <T> void countData(List<T> list,String idName, String parentIdName, List<String> targetSumDataNameList) {
        if(list != null && list.size() > 0){
            // 取出将最下级和其他数据分开
            Map<String,T> id_object = Maps.newHashMap();
            List<String> parent_id_list = Lists.newArrayList();
            for (T t : list) {
                Map<String, Object> map = gainData(t);
                id_object.put(map.get(idName).toString(),t);
                if(!StringUtils.isEmpty(map.get(parentIdName)) && !"0".equalsIgnoreCase(map.get(parentIdName).toString())){
                    parent_id_list.add(map.get(parentIdName).toString());
                }
            }
            if(parent_id_list != null && parent_id_list.size() > 0){
                List<T> tree_up_list = new ArrayList<>();
                List<T> tree_last_list = new ArrayList<>();
                for (String id : id_object.keySet()){
                    if(parent_id_list.contains(id)){
                        tree_up_list.add(id_object.get(id));
                    }else {
                        tree_last_list.add(id_object.get(id));
                    }
                }
                Map<String,T> id_tree_up_list = Maps.newHashMap();
                Map<String,List<T>> parent_id_tree_last_list = Maps.newHashMap();
                for (T t : tree_up_list) {
                    Map<String, Object> map = gainData(t);
                    id_tree_up_list.put(map.get(idName).toString(),t);
                }
                for (T t : tree_last_list) {
                    Map<String, Object> map = gainData(t);
                    if(parent_id_tree_last_list.containsKey(map.get(parentIdName).toString())){
                        parent_id_tree_last_list.get(map.get(parentIdName).toString()).add(t);
                    }else {
                        parent_id_tree_last_list.put(map.get(parentIdName).toString(),new ArrayList<>());
                        parent_id_tree_last_list.get(map.get(parentIdName).toString()).add(t);
                    }
                }
                for (String parent_id : parent_id_tree_last_list.keySet()){
                    List<T> treeList = parent_id_tree_last_list.get(parent_id);
                    T t = id_tree_up_list.get(parent_id);
                    for (T tree : treeList) {
                        try {
                            Class<?> clazz = tree.getClass();
                            for (String targetSumDataName : targetSumDataNameList) {
                                Field field = clazz.getDeclaredField(targetSumDataName);
                                field.setAccessible(true);
                                if(StringUtils.isEmpty(field.get(tree).toString())){
                                    field.set(tree,0.0);
                                }
                                Class<?> tClass = t.getClass();
                                Field target = tClass.getDeclaredField(targetSumDataName);
                                target.setAccessible(true);
                                target.set(t, BigDecimal.valueOf(Double.parseDouble(target.get(t).toString())).add(BigDecimal.valueOf(Double.parseDouble(field.get(tree).toString()))).setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue());
                            }
                        } catch (NoSuchFieldException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
                countData(tree_up_list,idName,parentIdName,targetSumDataNameList);
            }
        }
    }

    // 获取数据
    private static <T> Map<String,Object> gainData(T t){
        Map<String,Object> data = Maps.newHashMap();
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object o = field.get(t);
                data.put(field.getName(),o);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return data;
    }




}